package com.wzapp.sitposture.common.aop;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.wzapp.sitposture.common.error.CustomizedException;
import com.wzapp.sitposture.common.exception.AuthenticationException;
import com.wzapp.sitposture.common.exception.AuthorizationException;
import com.wzapp.sitposture.common.exception.BadRequestException;
import com.wzapp.sitposture.common.exception.NotfoundException;
import com.wzapp.sitposture.common.util.IPAddressUtil;
import com.wzapp.sitposture.common.util.ToolUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.URLDecoder;
import java.security.Principal;
import java.util.Arrays;

@Aspect
@Component
public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    private Environment env;

    @Pointcut("execution(public * com.wzapp.sitposture..endpoint.*.*(..))")//两个..代表所有子目录，最后括号里的两个..代表所有参数
    public void logPointCut() {
    }

    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {

        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容
        String url = request.getRequestURL().toString();

        StringBuilder logStr = new StringBuilder("线程ID：").append(Thread.currentThread().getId())
                .append(", 请求地址 : ").append(url)
                .append(", Referer : ").append(request.getHeader("Referer"))
                .append(", HTTP METHOD : ").append(request.getMethod())
                .append(", IP :").append(IPAddressUtil.getClientIpAddress(request))
                .append(", CLASS_METHOD : ").append(joinPoint.getSignature().getDeclaringTypeName()).append(","+joinPoint.getSignature().getName());

        Principal principal = request.getUserPrincipal();
        if (null != principal) {
            String deviceInfo = request.getHeader("deviceInfo");
            if (ToolUtil.isNotEmpty(deviceInfo)){
                logStr.append(",Principal is:").append(principal.getName()).append(",access_token:").append(request.getParameter("access_token")).append(",deviceInfo:"+URLDecoder.decode(deviceInfo,"UTF-8"));
            }else {
                logStr.append(",Principal is:").append(principal.getName()).append(",access_token:").append(request.getParameter("access_token"));
            }
        } else {
            logStr.append(",Principal is NULL");
        }

        //输出参数到日志。
        Object[] args = joinPoint.getArgs();
        if (args != null) {

            try {
                logStr.append(",参数:").append(JSONObject.toJSONString(args));
            }
            catch (JSONException e) {
                //普通参数传入
                logStr.append(",参数 :").append(Arrays.toString(args));
            }
        }

        logger.info(logStr.toString());
    }

    @AfterReturning(returning = "ret", pointcut = "logPointCut()")
    public void doAfterReturning(Object ret) throws Throwable {
    }

    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object ob = pjp.proceed();// ob 为方法的返回值
        logger.debug("线程ID：{}, name:{} ______end______. 耗时 :{} ms.",
                Thread.currentThread().getId(),
                Thread.currentThread().getName(),
                (System.currentTimeMillis() - startTime));
        return ob;
    }

    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void afterThrowing(Throwable e) {

        // 关闭一些不必要的异常打印
        if (e instanceof CustomizedException) {
            printExceptionLine(e, " ==CustomizedException: ");
        } else if (e instanceof NotfoundException){
            printExceptionLine(e, " ==NotfoundException: ");
        } else if (e instanceof AuthorizationException){
            printExceptionLine(e, " ==AuthorizationException: ");
        } else if (e instanceof AuthenticationException){
            printExceptionLine(e, " ==AuthenticationException: ");
        } else if (e instanceof BadRequestException){
            printExceptionLine(e, " ==BadRequestException: ");
        } else {
            logger.error("线程ID：" + Thread.currentThread().getId() +"日志中发现异常 Exception", e);
        }
    }

    private void printExceptionLine(Throwable e, String s) {

        StringBuffer sb = new StringBuffer();
        StackTraceElement ste = e.getStackTrace()[0];
        if (null != ste) {
            logger.info("========深度："+e.getStackTrace().length);
            sb.append("线程ID：").append(Thread.currentThread().getId()).append(s).append(ste.getClassName()).append(" [").append(ste.getFileName()).append(":").append(ste.getLineNumber()).append(" ] methodName:").append(ste.getMethodName()).append(" message:").append(e.getMessage());
        }
        logger.error(sb.toString());
    }
}
